﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Web.Security;

namespace AnyRepMembershipProvider
{
    public class AnyRepMembershipConfig : IAnyRepMembershipConfig
    {
        private const string CONFIG_KEY_APPLICATION_NAME = "applicationName";
        private const string CONFIG_KEY_DESCRIPTION = "description";
        private const string CONFIG_KEY_ENABLE_PASSWORD_RESET = "enablePasswordReset";
        private const string CONFIG_KEY_ENABLE_PASSWORD_RETRIEVAL = "enablePasswordRetrieval";
        private const string CONFIG_KEY_MAX_INVALID_PASSWORD_ATTEMPTS = "maxInvalidPasswordAttempts";
        private const string CONFIG_KEY_MIN_REQUIRED_NON_ALPHANUMERIC_CHARACTERS = "minRequiredNonAlphanumericCharacters";
        private const string CONFIG_KEY_MIN_REQUIRED_PASSWORD_LENGTH = "minRequiredPasswordLength";
        private const string CONFIG_KEY_PASSWORD_ATTEMPT_WINDOW = "passwordAttemptWindow";
        private const string CONFIG_KEY_PASSWORD_FORMAT = "passwordFormat";
        private const string CONFIG_KEY_PASSWORD_STRENGTH_REGULAR_EXPRESSION = "passwordStrengthRegularExpression";
        private const string CONFIG_KEY_REPOSITORY_ASSEMBLY_NAME = "repositoryAssemblyName";
        private const string CONFIG_KEY_REPOSITORY_CLASS_NAME = "repositoryClassName";
        private const string CONFIG_KEY_REQUIRES_QUESTION_AND_ANSWER = "requiresQuestionAndAnswer";
        private const string CONFIG_KEY_REQUIRES_UNIQUE_EMAIL = "requiresUniqueEmail";

        internal NameValueCollection configValues;

        /// <summary>
        /// Creates an instance of this class
        /// </summary>
        /// <param name="configValues">A NameValueCollection with the configuration Parameters</param>
        public AnyRepMembershipConfig(NameValueCollection configValues)
        {
            this.configValues = configValues;
            this._applicationName = GetConfigValueOrDefault(CONFIG_KEY_APPLICATION_NAME, System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
        }

        #region Helpers

        /// <summary>
        /// Gets a Configuration value if valid or a default value
        /// </summary>
        /// <param name="configValueKey">The key for the current value being seek on the collection</param>
        /// <param name="defaultValue">The Default Value</param>
        private string GetConfigValueOrDefault(string configValueKey, string defaultValue)
        {
            // Check for empty config value
            if (string.IsNullOrEmpty(configValues[configValueKey]))
                return defaultValue;

            return configValues[configValueKey];
        }

        /// <summary>
        /// Gets a configuration value if valid or a default value as an integer
        /// </summary>
        /// <param name="configValueKey">The key for the current value being seek on the collection</param>
        /// <param name="defaultValue">The Default Value</param>
        private int GetConfigValueOrDefault(string configValueKey, int defaultValue)
        {
            // Check for empty config value
            if (string.IsNullOrEmpty(configValues[configValueKey]))
                return defaultValue;

            // Check if can parse the configuration value as int
            int retVal;
            if (int.TryParse(configValues[configValueKey], out retVal))
                return retVal;
            else
                return defaultValue;
        }

        /// <summary>
        /// Gets a configuration value if valid or a default value as a boolean
        /// </summary>
        /// <param name="configValueKey">The key for the current value being seek on the collection</param>
        /// <param name="defaultValue">The Default Value</param>
        private bool GetConfigValueOrDefault(string configValueKey, bool defaultValue)
        {
            // Check for empty config value
            if (string.IsNullOrEmpty(configValues[configValueKey]))
                return defaultValue;

            // Check if can parse the configuration value as boolean
            bool retVal;
            if (bool.TryParse(configValues[configValueKey], out retVal))
                return retVal;
            else
                return defaultValue;
        }

        /// <summary>
        /// Gets a Configuration value if valid or throws a ProviderBase Exception with the message
        /// </summary>
        /// <param name="configValueKey">The key for the current value being seek on the collection</param>
        /// <param name="exceptionMessage">The message related to the ProviderBase Exception</param>
        private string GetConfigValueOrProviderException(string configValueKey, string exceptionMessage)
        {
            if (string.IsNullOrWhiteSpace(configValues[configValueKey]))
                throw new ProviderException(exceptionMessage);

            return configValues[configValueKey];
        }

        /// <summary>
        /// Returns a password format enum object based on the string
        /// </summary>
        /// <param name="passwordFormat">A string representing the password format</param>
        private MembershipPasswordFormat GetPasswordFormatFromString(string passwordFormat)
        {
            switch (passwordFormat)
            {
                case "Hashed":
                    return MembershipPasswordFormat.Hashed;
                case "Encrypted":
                    return MembershipPasswordFormat.Encrypted;
                case "Clear":
                    return MembershipPasswordFormat.Clear;
                default:
                    throw new ProviderException("No or invalid password format");
            }
        }

        #endregion

        private string _applicationName;
        public string ApplicationName
        {
            get { return _applicationName; }
            set { _applicationName = value; }
        }

        public string Description
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_DESCRIPTION, "Composite Membership Provider is generic provider which plugs into a repository class"); }
        }

        public bool RequiresUniqueEmail
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_REQUIRES_UNIQUE_EMAIL, true); }
        }

        public int MaxInvalidPasswordAttempts
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_MAX_INVALID_PASSWORD_ATTEMPTS, 5); }
        }

        public int PasswordAttemptWindow
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_PASSWORD_ATTEMPT_WINDOW, 10); }
        }

        public int MinRequiredNonAlphanumericCharacters
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_MIN_REQUIRED_NON_ALPHANUMERIC_CHARACTERS, 0); }
        }

        public int MinRequiredPasswordLength
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_MIN_REQUIRED_PASSWORD_LENGTH, 6); }
        }

        public string PasswordStrengthRegularExpression
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_PASSWORD_STRENGTH_REGULAR_EXPRESSION, string.Empty); }
        }   

        public bool EnablePasswordReset
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_ENABLE_PASSWORD_RESET, true); }
        }

        public bool EnablePasswordRetrieval
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_ENABLE_PASSWORD_RETRIEVAL, false); }
        }

        public bool RequiresQuestionAndAnswer
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_REQUIRES_QUESTION_AND_ANSWER, false); }
        }

        public MembershipPasswordFormat PasswordFormat
        {
            get { return GetPasswordFormatFromString(configValues[CONFIG_KEY_PASSWORD_FORMAT]); }
        }

        public string RepositoryAssemblyName
        {
            get { return GetConfigValueOrDefault(CONFIG_KEY_REPOSITORY_ASSEMBLY_NAME, string.Empty); }
        }

        public string RepositoryClassName
        {
            get { return GetConfigValueOrProviderException(CONFIG_KEY_REPOSITORY_CLASS_NAME, "No repository class name was provided"); }
        }
    }
}
